﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="AbstractBroadPhase.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2010, 2011
//       Roland Rosenkranz (Glatzemann@email.de)
// </copyright>
// <license>
//   This file is part of starLiGHT.Collision.
//
//   starLiGHT.Collision is free software: you can redistribute it and/or modify
//   it under the terms of the GNU Lesser General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   starLiGHT.Collision is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public License
//   along with starLiGHT.Collision. If not, see http://www.gnu.org/licenses/.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.Collision are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 3345                  $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2011-01-13 16:01:08 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT.Collision.BroadPhase
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Microsoft.Xna.Framework;
    using starLiGHT.Collections;
    #endregion

    public abstract class AbstractBroadPhase : IBroadPhase
    {
        public int TestCount { get; protected set; }
        
        public abstract bool Test(ICollidable2D a, ICollidable2D b);

        public abstract bool Test(ICollidable3D a, ICollidable3D b);

        public virtual void BuildCollisionPairs(CollisionManager manager, ref ICollidable[] collidables)
        {
            if (collidables == null)
            {
                return;
            }

            if (manager == null)
            {
                throw new ArgumentNullException("manager");
            }

            ICollidable a;
            ICollidable b;

            // *****************
            // * add new pairs *
            // *****************
            this.TestCount = 0;
            int collidableCount = collidables.Length;
            for (ushort i = 0; i < collidableCount; i++)
            {
                a = collidables[i];
                if (a != null)
                {
                    for (ushort j = (ushort)(i + 1); j < collidableCount; j++)
                    {
                        b = collidables[j];
                        if (b != null)
                        {
                            if (a != b && (!a.Static || !b.Static) && this.Test(a, b))
                            {
                                manager.PairManager.AddPair(i, j, a, b);
                            }
                        }
                    }
                }
            }
        }

        public virtual void CleanupPairs(CollisionManager manager)
        {
            if (manager == null)
            {
                throw new ArgumentNullException("manager");
            }

            foreach (UserPair<ICollidable> pair in manager.PairManager.Pairs)
            {
                if (!this.Test(pair.object0, pair.object1))
                {
                    manager.PairManager.RemovePair(pair.ID0, pair.ID1);
                    manager.OnCollisionResolved(pair);
                }
            }
        }

        public abstract bool CheckCompatibility(ICollidable c);

        public virtual void RegisterObject(ICollidable2D c)
        {
        }

        public virtual void RegisterObject(ICollidable3D c)
        {
        }

        public int IndexOfCollidable(ref ICollidable[] collidables, ICollidable c)
        {
            for (int i = 0; i < collidables.Length; i++)
            {
                if (collidables[i] != null && collidables[i].Equals(c))
                {
                    return i;
                }
            }

            return -1;
        }

        private bool Test(ICollidable a, ICollidable b)
        {
            this.TestCount++;

            if (a is ICollidable2D && b is ICollidable2D)
            {
                return this.Test(a as ICollidable2D, b as ICollidable2D);
            }
            else if (a is ICollidable3D && b is ICollidable3D)
            {
                return this.Test(a as ICollidable3D, b as ICollidable3D);
            }

            throw new InvalidOperationException("you must not mix ICollidable2D and ICollidable3D");
        }
    }
}
